/*
 * Copyright 2013 Rivaldo.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jety.client.transport;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import org.jety.commons.exception.JsonRpcClientException;

/**
 * The client transport for HTTP request.
 * @author Rivaldo
 */
public class HttpClientTransport {

    private static final int BUFF_LENGTH = 1024;

    private final URL url;
    private final Map<String, String> headers;

    /**
     * Create a new <code>HttpClientTransport</HttpClientTransport>.
     * @param url to acess the server
     */
    public HttpClientTransport(URL url) {
        this.url = url;
        this.headers = new HashMap<String, String>();
    }
    
    /**
     * Add a custom http header.
     * @param key the header key
     * @param value the header value
     */
    public final void setHeader(String key, String value) {
        this.headers.put(key, value);
    }

    public final String call(String requestData) throws Exception {
        return sendRequest(url, headers, requestData);
    }

    private String sendRequest(URL url, Map<String, String> headers, String data) throws IOException {

        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        createHeader(headers, connection);

        connection.connect();
        sendRequest(connection, data);

        return tryReadResonse(connection);
    }

    //<editor-fold defaultstate="collapsed" desc="comment">
    private void createHeader(Map<String, String> headers, HttpURLConnection connection) throws ProtocolException {
        
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                connection.addRequestProperty(entry.getKey(), entry.getValue());
            }
        }
        
        connection.addRequestProperty("Accept-Encoding", "gzip");
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
    }
    
    private InputStream getInputStream(HttpURLConnection connection) throws IOException {
        
        String responseEncoding = connection.getHeaderField("Content-Encoding");
        responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
        
        InputStream in = connection.getInputStream();
        
        if ("gzip".equalsIgnoreCase(responseEncoding)) {
            in = new GZIPInputStream(in);
        }
        
        in = new BufferedInputStream(in);
        return in;
    }
//</editor-fold>

    private void sendRequest(HttpURLConnection connection, String data) throws JsonRpcClientException, IOException {

        OutputStream out = null;

        try {
            out = connection.getOutputStream();

            out.write(data.getBytes());
            out.flush();
            out.close();

            checkRequestResponse(connection);
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    private void checkRequestResponse(HttpURLConnection connection) throws JsonRpcClientException, IOException {
        int statusCode = connection.getResponseCode();
        if (statusCode != HttpURLConnection.HTTP_OK) {
            throw new JsonRpcClientException("Unexpected status code returned : " + statusCode);
        }
    }

    private String tryReadResonse(HttpURLConnection connection) throws IOException {

        InputStream inputStream = null;

        try {
            inputStream = getInputStream(connection);
            return read(inputStream);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    private String read(InputStream in) throws IOException {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        byte[] buff = new byte[BUFF_LENGTH];
        int n;

        while ((n = in.read(buff)) > 0) {
            bos.write(buff, 0, n);
        }

        bos.flush();
        bos.close();

        return bos.toString();
    }

    public URL getUrl() {
        return url;
    }
}
